#include "pch.h"
#include "VP8Encoder.h"

VP8Encoder::VP8Encoder(int width, int height, int fps, int bitrate, VideoEncDataObserver* p)
    : inputWidth_(width), inputHeight_(height), _ob_(p) {

    vpx_codec_err_t res = vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &cfg_, 0);
    if (res) {
        log_error( "Failed to get default VP8 config: %s\n", vpx_codec_err_to_string(res));
        return;
    }

    // Set the encoder parameters
    cfg_.g_w = width;
    cfg_.g_h = height;
    cfg_.g_timebase.num = 1;
    cfg_.g_timebase.den = fps;
    cfg_.rc_target_bitrate = bitrate; // VP8 expects bitrate in kbps

    // Create the encoder
    res = vpx_codec_enc_init(&encoder_, vpx_codec_vp8_cx(), &cfg_, 0);
    if (res) {
        log_error( "Failed to initialize VP8 encoder: %s\n", vpx_codec_err_to_string(res));
        return;
    }
    isInit = true;
    // Allocate the image
    vpx_img_alloc(&img_, VPX_IMG_FMT_I420, width, height, 1);
}

VP8Encoder::~VP8Encoder() {
    vpx_img_free(&img_);
    vpx_codec_destroy(&encoder_);
}

void VP8Encoder::encode(void* data, size_t dataSize, long long pts) {
    if (!isInit) {
        log_error( "Encoder is not initialized\n");
        return ;
    }

    // Fill the VPX image with input data
    memcpy(img_.planes[VPX_PLANE_Y], data, inputWidth_ * inputHeight_);
    memcpy(img_.planes[VPX_PLANE_U], (unsigned char*)data+ inputWidth_ * inputHeight_, (inputWidth_ / 2) * (inputHeight_ / 2));
    memcpy(img_.planes[VPX_PLANE_V], (unsigned char*)data + inputWidth_ * inputHeight_*5/4, (inputWidth_ / 2) * (inputHeight_ / 2));
    // Encode the frame
    vpx_codec_err_t res = vpx_codec_encode(&encoder_, &img_, pts, 1, 0, VPX_DL_REALTIME);
    if (res) {
        log_error( "Encoding failed: %s\n", vpx_codec_err_to_string(res));
        return ;
    }

    // Get encoded data
    vpx_codec_iter_t iter = nullptr;
    const vpx_codec_cx_pkt_t* pkt;
    while ((pkt = vpx_codec_get_cx_data(&encoder_, &iter))) {
        if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
            // Notify observer with encoded data
            if (_ob_) {
                _ob_->onVideoEncodeData(pkt->data.frame.buf, pkt->data.frame.sz,pkt->data.frame.pts, pkt->data.frame.pts);
            }
        }
    }
}
